// MapMakerDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MapMaker.h"
#include "MapMakerDlg.h"
#include "MapfileParser.h"
#include "Dijkstras.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CMapMakerDlg dialog

CMapMakerDlg::CMapMakerDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CMapMakerDlg::IDD, pParent), m_dlgMap(&m_vecNodes)
{
    m_mode = MODE_NONE;
    m_nodeCount = 0;
    m_cnode = NULL;
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMapMakerDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_EDIT1, m_dlgNodeName);
    DDX_Control(pDX, IDC_BUTTON1, m_dlgNodeNorth);
    DDX_Control(pDX, IDC_BUTTON2, m_dlgNodeEast);
    DDX_Control(pDX, IDC_BUTTON3, m_dlgNodeSouth);
    DDX_Control(pDX, IDC_BUTTON5, m_dlgNodeWest);
    DDX_Control(pDX, IDC_NYC_MAP, m_dlgMap);
    DDX_Control(pDX, IDC_CHECK1, m_dlgAddMode);
    DDX_Control(pDX, IDC_CHECK2, m_dlgInletCheck);
    DDX_Control(pDX, IDC_CHECK3, m_dlgOutletCheck);
    DDX_Control(pDX, IDC_EDIT2, m_dlgDesc);
}

BEGIN_MESSAGE_MAP(CMapMakerDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
    ON_STN_CLICKED(IDC_NYC_MAP, &CMapMakerDlg::OnClicked_NycMap)
    ON_COMMAND(ID_MAP_CLEARMAP, &CMapMakerDlg::OnMenu_ClearMap)
    ON_BN_CLICKED(IDC_BUTTON1, &CMapMakerDlg::OnClicked_North)
    ON_BN_CLICKED(IDC_BUTTON2, &CMapMakerDlg::OnClicked_East)
    ON_BN_CLICKED(IDC_BUTTON3, &CMapMakerDlg::OnClicked_South)
    ON_BN_CLICKED(IDC_BUTTON5, &CMapMakerDlg::OnClicked_West)
    ON_COMMAND(ID_FILE_SAVE32771, &CMapMakerDlg::OnFileSave)
    ON_BN_CLICKED(IDC_BUTTON4, &CMapMakerDlg::OnClicked_DeleteNode)
    ON_BN_CLICKED(IDC_CHECK2, &CMapMakerDlg::OnClicked_Inlet)
    ON_BN_CLICKED(IDC_CHECK3, &CMapMakerDlg::OnClicked_Outlet)
    ON_BN_CLICKED(IDC_BUTTON7, &CMapMakerDlg::OnClicked_NudgeUp)
    ON_BN_CLICKED(IDC_BUTTON6, &CMapMakerDlg::OnClicked_NudgeRight)
    ON_BN_CLICKED(IDC_BUTTON8, &CMapMakerDlg::OnClicked_NudgeDown)
    ON_BN_CLICKED(IDC_BUTTON9, &CMapMakerDlg::OnCicked_NudgeLeft)
    ON_COMMAND(ID_FILE_OPEN32775, &CMapMakerDlg::OnFileOpen)
    ON_WM_DESTROY()
END_MESSAGE_MAP()


// CMapMakerDlg message handlers

BOOL CMapMakerDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here
    UpdateInfo();
	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CMapMakerDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CMapMakerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CMapMakerDlg::UpdateInfo()
{
    bool enableWindows = true;

    if (!m_cnode)
    {
        enableWindows = false;
        m_dlgNodeName.SetWindowText("");
        m_dlgNodeNorth.SetWindowText("North (&t): None");
        m_dlgNodeEast.SetWindowText("East (&h): None");
        m_dlgNodeSouth.SetWindowText("South (&g): None");
        m_dlgNodeWest.SetWindowText("West (&f): None");
        m_dlgInletCheck.EnableWindow(FALSE);
        m_dlgOutletCheck.EnableWindow(FALSE);
    }
    else
    {
        CString strTemp;
        strTemp.Format("%d", m_cnode->m_id);
        m_dlgNodeName.SetWindowText(strTemp);

        m_dlgInletCheck.EnableWindow(TRUE);
        m_dlgInletCheck.SetCheck(m_cnode->m_type & NF_INLET);
        m_dlgOutletCheck.EnableWindow(TRUE);
        m_dlgOutletCheck.SetCheck(m_cnode->m_type & NF_OUTLET);


        if (m_cnode->m_north)
        {
            strTemp.Format("North (&t): %d", m_cnode->m_north->m_id);
            m_dlgNodeNorth.SetWindowText(strTemp);
        }
        else
            m_dlgNodeNorth.SetWindowText("North (&t): None");

        if (m_cnode->m_east)
        {
            strTemp.Format("East (&h): %d", m_cnode->m_east->m_id);
            m_dlgNodeEast.SetWindowText(strTemp);
        }
        else
            m_dlgNodeEast.SetWindowText("East (&h): None");

        if (m_cnode->m_south)
        {
            strTemp.Format("South (&g): %d", m_cnode->m_south->m_id);
            m_dlgNodeSouth.SetWindowText(strTemp);
        }
        else
            m_dlgNodeSouth.SetWindowText("South (&g): None");

        if (m_cnode->m_west)
        {
            strTemp.Format("West (&f): %d", m_cnode->m_west->m_id);
            m_dlgNodeWest.SetWindowText(strTemp);
        }
        else
            m_dlgNodeWest.SetWindowText("West (&f): None");   
    }

    m_dlgNodeName.EnableWindow(enableWindows);
    m_dlgNodeSouth.EnableWindow(enableWindows);
    m_dlgNodeWest.EnableWindow(enableWindows);
    m_dlgNodeEast.EnableWindow(enableWindows);
    m_dlgNodeNorth.EnableWindow(enableWindows);
    
    m_dlgMap.Invalidate();
}

CPoint CMapMakerDlg::Gridalize(const CPoint& pt)
{
    CPoint point(pt);
    for (CNodePtrVecIter it = m_vecNodes.begin(); it != m_vecNodes.end(); ++it)
    {
        CNode* n = *it;
        if (abs(n->m_pt.x - point.x) < SELECT_RADIUS)
        {
            point.x = n->m_pt.x;
        }
        
        if (abs(n->m_pt.y - point.y) < SELECT_RADIUS)
        {
            point.y = n->m_pt.y;
        }
    }

    return point;
}

void CMapMakerDlg::NewNode(CPoint& pt)
{    
    CNode* n = new CNode();
    n->m_pt = Gridalize(pt);
    n->m_id = m_nodeCount++;

    // Add to vector and mark as current node
    m_vecNodes.push_back(n);

    // Make current
    SetCurNode(n);
}

void CMapMakerDlg::ToBmpCoords(CPoint& pt)
{
    CRect rect;
    GetDlgItem(IDC_NYC_MAP)->GetWindowRect(&rect);
    pt.x -= rect.TopLeft().x;
    pt.y -= rect.TopLeft().y;
}

void CMapMakerDlg::OnClicked_NycMap()
{
    CPoint pt;
    GetCursorPos(&pt);
    
    ToBmpCoords(pt);

    // Check if click was a selection
    for (CNodePtrVecIter it = m_vecNodes.begin(); it != m_vecNodes.end(); ++it)
    {
        CNode* n = *it;
        if ( abs(pt.x - n->m_pt.x) <= SELECT_RADIUS && abs(pt.y - n->m_pt.y) <= SELECT_RADIUS)
        {
            if (m_mode != MODE_NONE)
            {
                ConnectNode(n);
            }
            else
            {
                SetCurNode(n);
            }

            return;
        }
    }

    // Click wasn't a selection, deselect or create a new node
    if (m_mode != MODE_NONE)
    {
        ConnectNode(NULL);
    }
    else
    {
        if (m_dlgAddMode.GetCheck())
        {
            // It wasn't a selection, create a new node
            CRect rect;
            NewNode(pt);
        }
        else
        {
            SetCurNode(NULL);
        }
    }
}

void CMapMakerDlg::SetCurNode(CNode* n)
{    
    if (m_cnode)
    {
        m_cnode->m_type &= ~NF_SELECTED;
    }

    m_cnode = n;

    if (m_cnode)
    {
        m_cnode->m_type |= NF_SELECTED;
    }

    UpdateInfo();
}

void CMapMakerDlg::ConnectNode(CNode* n)
{
    switch (m_mode)
    {
        case MODE_CON_NORTH: 
            m_cnode->m_north = n;
            //n->m_south = m_cnode;
            break;
        case MODE_CON_EAST: 
            m_cnode->m_east = n;
            //n->m_west = m_cnode;
            break;
        case MODE_CON_SOUTH: 
            m_cnode->m_south = n;
            //n->m_north = m_cnode;
            break;
        case MODE_CON_WEST: 
            m_cnode->m_west = n;
            //n->m_east = m_cnode;
            break;
        default:
            TRACE("Invalid mode for node interconnection\n");
            ASSERT(FALSE);
            break;
    }

    m_mode = MODE_NONE;
    UpdateInfo();
}

void CMapMakerDlg::OnMenu_ClearMap()
{
    m_vecNodes.clear();
    m_cnode = NULL;    
    m_nodeCount = 0;
    UpdateInfo();
}

void CMapMakerDlg::OnClicked_North()
{
    if (m_mode == MODE_CON_NORTH)
        m_mode = MODE_NONE;
    else
        m_mode = MODE_CON_NORTH;
}

void CMapMakerDlg::OnClicked_East()
{
    if (m_mode == MODE_CON_EAST)
        m_mode = MODE_NONE;
    else
        m_mode = MODE_CON_EAST;
}

void CMapMakerDlg::OnClicked_South()
{
    if (m_mode == MODE_CON_SOUTH)
        m_mode = MODE_NONE;
    else
        m_mode = MODE_CON_SOUTH;
}


void CMapMakerDlg::OnClicked_West()
{
    if (m_mode == MODE_CON_WEST)
        m_mode = MODE_NONE;
    else
        m_mode = MODE_CON_WEST;
}

void CMapMakerDlg::OnFileOpen()
{
    CFileDialog openDlg(TRUE, ".txt", "", 0, "Text Files (*.txt)|*.txt||"); 
    if (openDlg.DoModal() == IDOK)
    {
        m_vecNodes.clear();
        CString desc = CMapfileParser::Parse(openDlg.GetPathName(), m_vecNodes);
        m_dlgDesc.SetWindowText(desc);
        m_nodeCount = m_vecNodes.size();
    }

    UpdateInfo();
}

void CMapMakerDlg::OnFileSave()
{
    // Write description
    CString desc;
    m_dlgDesc.GetWindowText(desc);
    if (desc.IsEmpty())
    {
        AfxMessageBox("Please enter a short description");
        m_dlgDesc.SetFocus();
        return;
    }

    CFileDialog saveDlg(FALSE, ".txt", "mapfile.txt", OFN_OVERWRITEPROMPT, "Text Files (*.txt)|*.txt||");
    if (saveDlg.DoModal() == IDOK)
    {    
        CStdioFile file(saveDlg.GetPathName(), CFile::modeWrite | CFile::modeCreate);

        CString descString;
        descString.Format("desc[%s]\n", desc);
        file.WriteString(descString);

        WriteNodes(file);
        WritePaths(file);
    }

}

void CMapMakerDlg::WriteNodes(CStdioFile& file)
{
    // Write each node
    for (CNodePtrVecIter it = m_vecNodes.begin(); it != m_vecNodes.end(); ++it)
    {
        CNode* n = *it;
        CString line;
        line.Format("node[%d](%d,%d,%d,%d):%d,%d,%d,%d\n", n->m_id, n->m_pt.x, n->m_pt.y, 
            (n->m_type & NF_INLET) == NF_INLET, (n->m_type & NF_OUTLET) == NF_OUTLET,  
            (n->m_north ? n->m_north->m_id : -1),
            (n->m_east ? n->m_east->m_id : -1),
            (n->m_south ? n->m_south->m_id : -1),
            (n->m_west ? n->m_west->m_id : -1));
        file.WriteString(line);
    }
}

void CMapMakerDlg::WritePaths(CStdioFile& file)
{
    int iPathCount = 0;

    // Initialize Dijkstra's
    CDijkstras dijk(&m_vecNodes);

    for (CNodePtrVecCIter itFrom = m_vecNodes.begin(); itFrom != m_vecNodes.end(); ++itFrom)
    {
        dijk.SetSource(*itFrom);

        for (CNodePtrVecCIter itTo = m_vecNodes.begin(); itTo != m_vecNodes.end(); ++itTo)
        {
            if (*itFrom == *itTo) continue;

            // Get Path
            CNodePtrVec vecPath;
            dijk.GetPathTo(*itTo, vecPath);

            // Format line
            CString line;
            line.Format("path[%d](%d,%d):",  iPathCount++, (*itFrom)->m_id, (*itTo)->m_id);
            for (CNodePtrVecIter it = vecPath.begin(); it != vecPath.end(); ++it)
            {
                line.AppendFormat("%d%c", (*it)->m_id, (*it == vecPath.back() ? '\n' : ','));
            }

            file.WriteString(line);
        }
    }
}

void CMapMakerDlg::OnClicked_DeleteNode()
{
    if (m_cnode)
    {
        CNodePtrVec tvec = m_vecNodes;
        m_vecNodes.clear();
        for (CNodePtrVecIter it = tvec.begin(); it != tvec.end(); ++it)
        {
            if (*it != m_cnode)
            {
                m_vecNodes.push_back(*it);
            }
        }
        
        // Disconnect adjascent nodes
        if (m_cnode->m_north) m_cnode->m_north->m_south = NULL;
        if (m_cnode->m_east) m_cnode->m_east->m_west = NULL;
        if (m_cnode->m_west) m_cnode->m_west->m_east = NULL;
        if (m_cnode->m_south) m_cnode->m_south->m_north = NULL;

        delete m_cnode;
        m_cnode = NULL;
    }

    UpdateInfo();
}

void CMapMakerDlg::OnClicked_Inlet()
{
    if (m_dlgInletCheck.GetCheck())
        m_cnode->m_type |= NF_INLET;
    else
        m_cnode->m_type &= ~NF_INLET;

    UpdateInfo();
}

void CMapMakerDlg::OnClicked_Outlet()
{
    if (m_dlgOutletCheck.GetCheck())
        m_cnode->m_type |= NF_OUTLET;
    else
        m_cnode->m_type &= ~NF_OUTLET;

    UpdateInfo();
}

void CMapMakerDlg::OnClicked_NudgeUp()
{
    m_cnode->m_pt.y--;
    UpdateInfo();
}

void CMapMakerDlg::OnClicked_NudgeRight()
{
    m_cnode->m_pt.x++;
    UpdateInfo();
}

void CMapMakerDlg::OnClicked_NudgeDown()
{
    m_cnode->m_pt.y++;
    UpdateInfo();
}

void CMapMakerDlg::OnCicked_NudgeLeft()
{
    m_cnode->m_pt.x--;
    UpdateInfo();
}

void CMapMakerDlg::OnDestroy()
{
    CDialog::OnDestroy();

    for (CNodePtrVecIter it = m_vecNodes.begin(); it != m_vecNodes.end(); ++it)
    {
        if (*it) delete *it;
    }
}
